package com.stockz.core.util;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import com.stockz.core.model.Bar;
import com.stockz.core.util.BarUtil.BarValueType;
import com.stockz.core.util.CoreConstant.TIME_UNIT;

public class TimeUnitUtil {

	private TimeUnitUtil() {
		// Singleton
	}

	private static double pointAndFigurePercent = 2.5;
	private static BarValueType pointAndFigureBarValueType = BarValueType.AVGPRICE;

	// Takes daily bar list
	public static List<Bar> changeTimeUnit(List<Bar> backupBarList,
			TIME_UNIT selectedItem) {
		if (backupBarList != null && !backupBarList.isEmpty()) {
			if (TIME_UNIT.DAILY.equals(selectedItem)) {
				return backupBarList;
			} else {
				ArrayList<Bar> barList = new ArrayList<Bar>();
				List<Bar> tempList = new ArrayList<Bar>();

				Date pivot = nextPivot(backupBarList, backupBarList.size() - 1,
						backupBarList.get(backupBarList.size() - 1).getDate(),
						selectedItem);

				for (int i = backupBarList.size() - 1; i >= 0; i--) {
					Bar bar = backupBarList.get(i);

					if (bar.getDate().before(pivot)) {
						tempList.add(bar);
					} else {
						Bar b = union(tempList);
						if (b != null) {
							barList.add(b);
						}
						tempList.clear();
						tempList.add(bar);
						pivot = nextPivot(backupBarList, i, pivot, selectedItem);
					}
				}
				if (!tempList.isEmpty()) {
					barList.add(union(tempList));
				}
				Collections.sort(barList);
				return barList;
			}

		}
		return null;
	}

	private static Date nextPivot(List<Bar> barList, int index, Date date,
			TIME_UNIT unit) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		switch (unit) {
		case DAILY:
			calendar.add(Calendar.DATE, 1);
			break;
		case WEEKLY:
			if (calendar.getFirstDayOfWeek() == calendar
					.get(Calendar.DAY_OF_WEEK)) {
				calendar.add(Calendar.DATE, 7);
			}
			while (calendar.getFirstDayOfWeek() != calendar
					.get(Calendar.DAY_OF_WEEK)) {
				calendar.add(Calendar.DATE, 1);
			}
			break;
		case MONTHLY:
			calendar.set(Calendar.DATE, 1);
			calendar.add(Calendar.MONTH, 1);
			break;
		case QUARTERLY:
			calendar.add(Calendar.MONTH, 3 - calendar.get(Calendar.MONTH) % 3);
			calendar.set(Calendar.DATE, 1);
			break;
		case YEARLY:
			calendar.set(Calendar.DATE, 1);
			calendar.set(Calendar.MONTH, Calendar.JANUARY);
			calendar.add(Calendar.YEAR, 1);
			break;
		case POINT_AND_FIGURE:
			double value = BarUtil.getValue(barList.get(index),
					pointAndFigureBarValueType);
			double high = value + (pointAndFigurePercent * value / 100);
			double low = value - (pointAndFigurePercent * value / 100);
			for (int i = index; i >= 0; i--) {
				double v = BarUtil.getValue(barList.get(i),
						pointAndFigureBarValueType);
				if (v < low || v > high) {
					return barList.get(i).getDate();
				}
			}
			return barList.get(0).getDate();
		}

		return calendar.getTime();
	}

	private static Bar union(List<Bar> barList) {
		Bar b = null;
		if (barList != null && !barList.isEmpty()) {
			b = new Bar();
			double high = 0;
			double low = Double.MAX_VALUE;
			double volume = 0;
			double trades = 0;
			double turnover = 0;
			b.setCode(barList.get(0).getCode());
			b.setOpen(barList.get(barList.size() - 1).getOpen());
			b.setLast(barList.get(0).getLast());
			b.setClose(barList.get(0).getClose());
			b.setDate(barList.get(0).getDate());
			for (Bar bar : barList) {
				if (high < bar.getHigh())
					high = bar.getHigh();
				if (low > bar.getLow())
					low = bar.getLow();
				volume += bar.getVolume();
				trades += bar.getTrades();
				turnover += bar.getTurnover();
			}

			b.setHigh(high);
			b.setLow(low);
			b.setVolume(volume);
			b.setTrades(trades);
			b.setTurnover(turnover);
		}
		return b;
	}

}
